在写代码的时候,无论是在公司还是自己写点东西,每次手里的代码写完了,本地环境也起来了,但是要部署到服务器上,得一个个命令敲,做多了不仅烦,而且还浪费时间,人肉 CI 真的难受
直到我接触了 GitHub Actions 一切都变得简单许多了,我再也不用一遍遍敲着哪些重复的命令,现在我把这些流程通过 yaml 文件,声明式的方式记录下来,“我的管家” GitHub Actions 就会帮我做,这多么棒啊!这样我就可以把时间和精力花在更重要的事情上了,因为重复枯燥,真的会把一个人给逼疯!
现在,公司项目用 GitLab Actions 部署测试/生产环境,而我个人的网站和 API 服务也是通过这个管家来管理自动构建发布,我把代码写完,推送到 Git 仓库之后,我的管家就开始为我干活了,生活变得美妙起来了
我不允许你还不知道这个东西,我将按下面的流程,将这个强大的管家介绍给你
首先,我们先来写一个最简单的 GitHub Actions 的 HelloWorld,先对它有个感性的认识
在开始之前,先把准备工作做好,创建一个空的 Git 仓库,然后在本地新建一个空文件夹。这一步我们暂时不写任何代码,直接在当前目录下创建文件:.github/workflows/helloworld.yaml
on: push
jobs:
checkout-files:
runs-on: ubuntu-latest
steps:
- run: pwd
- run: ls
check-node-version:
runs-on: ubuntu-latest
needs: checkout-files
steps:
- run: node -v
- run: npm -v这个 GitHub Actions workflow 在每次 push 时触发,包含两个 job:第一个 job 会列出当前工作目录和文件(pwd、ls),第二个 job 依赖于第一个,输出 node -v 与 npm -v,用于按顺序验证仓库内容与 Node/npm 运行环境
将本地仓库跟 Git 仓库关联,再 push 变更到 GitHub 之后,CI 就会自动触发

这里可以看到我们的 Helloworld 工作流执行成功,符合预期地输出了 node 和 npm 的版本号
在这个案例里,我们用 GitHub Actions 来实现 API 服务的全自动部署
流程大概是在 Actions 里 SSH 到服务器,编译并重启 systemd 服务。第一次部署的时候,需要先配置好 systemd 服务文件,后续只需推送代码即可自动完成构建、上传和重启
如果程序比较小,也可以直接在 Actions 中编译二进制文件并打包,再通过 SCP 上传到服务器,用 SSH 远程解压,最后重启 systemd 服务
.github/workflows/deploy.ymlname: Deploy Go Web API
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24.2"
- name: Build app
run: |
GOOS=linux GOARCH=amd64 go build -o api-go main.go
- name: Package binary
run: |
mkdir -p deploy
mv api-go deploy/
tar -czf api-go.tar.gz -C deploy api-go
- name: Upload to server
uses: appleboy/scp-action@v0.1.4
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
source: "api-go.tar.gz"
target: "/root/bin/"
- name: Remote restart service
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /root/bin
tar -xzf api-go.tar.gz
rm api-go.tar.gz
sudo systemctl restart api-go.service因为我们是用 systemd 来管理服务的,所以在第一次部署时,需要手动创建一个服务文件:/etc/systemd/system/api-go.service
[Unit]
Description=vodka API
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/root/bin
ExecStart=/root/bin/api-go
# 程序挂了自动重启
Restart=always
RestartSec=5
Environment=PORT=8081
[Install]
WantedBy=multi-user.target如果这是第一次部署应用,还需要额外配置一下 Nginx 反向代理。当然,这一步并不是必须的,只是为了让服务访问起来更方便和规范
server {
...
location /v3/ {
proxy_pass http://127.0.0.1:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port 443;
}
}验证配置并重启 Nginx
nginx -t
nginx -s reload后续发布部署流程:
/root/bin/api-go 编译后的可执行文件systemctl restart api-go.service 服务就会平滑更新,日志都能看见当然,这些活儿你都不用自己动手,管家 会全程替你搞定。你要做的就是写好代码,然后推到 Git 仓库,剩下的交给自动化流程就行
CI 被触发后,等待工作流执行完成。当然,你也可以在工作流的最后增加邮件或消息通知,提示程序构建成功
接着,我们可以登录到服务器上,检查应用的运行状态:
sudo systemctl status api-go
在这个案例里,我们首先需要准备一个应用。为了演示方便,我选择了一个 Next.js 应用,但你完全可以根据需求,选择任何前端或后端应用,都可以套用同样的自动化部署流程
流程如下:
我们要构建镜像,首先要做的就是编写 Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
CMD ["npm", "start"]然后配置一下 GitHub Actions 的工作流配置
on: push
jobs:
build-next:
runs-on: ubuntu-latest
steps:
- name: checkout-code
uses: actions/checkout@v4
- name: login-docker-hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: build-push-image
uses: docker/build-push-action@v5
with:
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/githubactions-docker:latest在推送代码之前,我们需要先把 DockerHub 配置加到 GitHub Actions 的 Secrets 里:

工作流执行成功后,可以在本地拉取镜像,验证容器是否正常启动
一般来说,云原生的构建发布流程是这么玩的:GitHub Actions 负责 CI 构建,ArgoCD 负责 CD 部署。至于 k8s 的部署文件,通常会放在一个独立的 Git 仓库里专门维护,这套玩法就叫 GitOps
简单理解,就是让 Git 驱动一切,让 ArgoCD 感知配置变化,驱动 k8s 自动滚动更新应用
name: Rum API Workflow
on:
push:
branches: [master]
env:
IMAGE_NAME: ccr.ccs.tencentyun.com/XXXX/rum-api
jobs:
build-and-push:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.bap.outputs.IMAGE_TAG }}
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Tencent Cloud Registry
run: echo "${{ secrets.CCR_PASSWORD }}" | docker login ccr.ccs.tencentyun.com --username=${{ secrets.CCR_USERNAME }} --password-stdin
- name: Build and Push Image
id: bap
run: |
TAG=$(date +%Y%m%d%H%M%S)
docker build -t $IMAGE_NAME:$TAG .
docker push $IMAGE_NAME:$TAG
echo "IMAGE_TAG=$TAG" >> $GITHUB_OUTPUT
deploy-to-k8s:
runs-on: ubuntu-latest
needs: build-and-push
steps:
- name: SSH into Server and Deploy
env:
IMAGE_TAG: ${{ needs.build-and-push.outputs.tag }}
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
export IMAGE_NAME=${{ env.IMAGE_NAME }}
export IMAGE_TAG=${{ env.IMAGE_TAG }}
cd /root/dev/rum-kustomize
kustomize edit set image $IMAGE_NAME:$IMAGE_TAG
kustomize build .
kubectl apply -k .为了方便教学演示,这里我先把 ArgoCD 拿掉。如果你在实际项目中用的是 ArgoCD,那就需要稍微改一下 deploy-to-k8s 中的 script 字段,主要逻辑是让它去同步 Git 仓库里的部署文件到最新版本。参考代码如下:
script:
- git clone $REMOTE_URL
- cd /root/dev/rum-kustomize
- kustomize edit set image $IMAGE_NAME:$IMAGE_TAG
- kustomize build .
- git commit -am '[skip ci] staging kustomize update'
- git push origin master当你编写好自己的 Kubernetes 应用声明文件后,还需要准备一个 kustomization.yml 文件。因为我们会通过 kustomize 来对 k8s 配置进行版本化管理与动态更新,从而完成部署与发布
resources:
- cm.yml
- deploy.yml
- svc.yml
images:
- name: ccr.ccs.tencentyun.com/XXXX/rum-api
newName: ccr.ccs.tencentyun.com/XXXX/rum-api
newTag: "2025XXXXXX"
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization至此,整个流程就算完成啦。最后只需要提交代码,触发 CI,等构建流程跑完后,你的应用就会在集群中自动完成滚动更新
